/**
 * \file StringRef.cpp
 * \brief StringRef declaration class
 * \author Edouard ROGE
 * \date 19 september 2010
 */

#include <cstring>
#include <stdlib.h>
#include "Core/Memory/MemoryPoolManager.h"
#include "Core/Log/LogManager.h"
#include "StringRef.h"

Mutex StringRef::m_Mutex=Mutex();

StringRef::StringRefNode::StringRefNode()
	: m_nRef(0), m_pStringNext(NULL)
{
}

StringRef::StringRefNode::StringRefNode( const char * _str )
	: m_nRef(1), m_String(_str), m_pStringNext(NULL)
{
}

StringRef::StringRefNode::StringRefNode( const String & _str )
	: m_nRef(1), m_String(_str), m_pStringNext(NULL)
{
}

StringRef::StringRefNode::~StringRefNode()
{
}

StringRef::StringRefNode * StringRef::m_pRootList = NULL;

StringRef::StringRef()
: m_Buffer(NULL)
{
}

StringRef::StringRef(char * s)
{
	Set(String(s));
}

StringRef::StringRef(const char * s)
{
	Set(String(s));
}

StringRef::StringRef(const String & _str)
{
	Set(_str);
}

StringRef::StringRef(const StringRef & _strRef)
{
	StringRefNode * pCurrent = m_pRootList;
	while( pCurrent )
	{
		if( pCurrent->m_String.GetData() == _strRef.m_Buffer )
		{
			pCurrent->m_nRef += 1;
			m_Buffer = pCurrent->m_String.GetData();
			return;
		}
		pCurrent = pCurrent->m_pStringNext;
	}

	LOG_ASSERT(false, "Unknown StringRef");
}

StringRef::~StringRef()
{
	Remove(m_Buffer);
}


char * StringRef::Insert(const String & _str)
{
	m_Mutex.Lock();
	StringRefNode * pPrev = NULL;
	StringRefNode * pCurrent = m_pRootList;
	while( pCurrent )
	{
		if( pCurrent->m_String == _str )
		{
			pCurrent->m_nRef += 1;
			m_Mutex.Unlock();
			return pCurrent->m_String.GetData();
		}
		pPrev = pCurrent;
		pCurrent = pCurrent->m_pStringNext;
	}

	pCurrent = (StringRefNode*)MemoryPoolManager::GetInstance().Allocate( sizeof(StringRefNode) );
	new(pCurrent)StringRefNode(_str);

	if( pPrev )
		pPrev->m_pStringNext = pCurrent;
	else
		m_pRootList = pCurrent;

	m_Mutex.Unlock();

	return pCurrent->m_String.GetData();
}

void StringRef::Remove(const char * _str)
{
	m_Mutex.Lock();
	StringRefNode * pPrev = NULL;
	StringRefNode * pCurrent = m_pRootList;
	while( pCurrent )
	{
		if( pCurrent->m_String == _str )
		{
			pCurrent->m_nRef -= 1;
			if( pCurrent->m_nRef == 0 )
			{
				if( pCurrent == m_pRootList )
					m_pRootList = pCurrent->m_pStringNext;
				if( pPrev )
					pPrev->m_pStringNext = pCurrent->m_pStringNext;
				pCurrent->~StringRefNode();
				MemoryPoolManager::GetInstance().Free( pCurrent );
				m_Mutex.Unlock();
				return;
			}
		}
		pPrev = pCurrent;
		pCurrent = pCurrent->m_pStringNext;
	}
	m_Mutex.Unlock();
}

void StringRef::Set(const String & _str)
{
	m_Buffer = Insert(_str);
}

StringRef & StringRef::operator = (char * _str)
{
	if( m_Buffer )
		Remove(m_Buffer);
	Set(String(_str));
	return (*this);
}

StringRef & StringRef::operator = (const char * _str)
{
	if( m_Buffer )
		Remove(m_Buffer);
	Set(String(_str));
	return (*this);
}

StringRef & StringRef::operator = (const String & _str)
{
	if( m_Buffer )
		Remove(m_Buffer);
	Set(_str);
	return (*this);
}

StringRef & StringRef::operator = (const StringRef & _str)
{
	return (*this);
}

StringRef::operator const char * () const
{
	return m_Buffer;
}

uint32 StringRef::GetLength() const
{
	return ( m_Buffer ? strlen(m_Buffer) : 0 );
}

/**
 * \fn String operator+ (const StringRef & _str1, const StringRef & _str2);
 * \param _str1
 * \param _str2
 * \return new string with the 2 params concatenated
 */
String operator+( const StringRef & _str1, const StringRef & _str2 )
{
	String res;
	res += _str1.m_Buffer;
	res += _str2.m_Buffer;

	return res;
}

/**
 * \fn String operator+ (const StringRef & _str1, const String & _str2);
 * \param _str1
 * \param _str2
 * \return new string with the 2 params concatenated
 */
String operator+ (const StringRef & _str1, const String & _str2)
{
	return _str1.m_Buffer + _str2;
}

/**
 * \fn String operator+ (const String & _str1, const StringRef & _str2);
 * \param _str1
 * \param _str2
 * \return new string with the 2 params concatenated
 */
String operator+ (const String & _str1, const StringRef & _str2)
{
	return _str1 + _str2.m_Buffer;

}

/**
 * \fn bool operator== (const StringRef & _str1, const StringRef & _str2);
 * \param _str1 first string to compare
 * \param _str2 second string to compare
 * \return true if _str1 and _str2 are equals
 */
bool operator== (const StringRef & _str1, const StringRef & _str2)
{
	return _str1.m_Buffer == _str2.m_Buffer;
}

/**
 * \fn bool operator!= (const StringRef & _str1, const StringRef & _str2);
 * \param _str1 first string to compare
 * \param _str2 second string to compare
 * \return true if _str1 and _str2 are not equals
 */
bool operator!= (const StringRef & _str1, const StringRef & _str2)
{
	return _str1.m_Buffer != _str2.m_Buffer;
}

/**
 * \fn bool operator== (const StringRef & _str1, const String & _str2);
 * \param _str1 first string to compare
 * \param _str2 second string to compare
 * \return true if _str1 and _str2 are equals
 */
bool operator== (const StringRef & _str1, const String & _str2)
{
	uint32 iLength1 = _str1.GetLength();
	uint32 iLength2 = _str2.GetLength();

	if( iLength1 != iLength2 )
		return false;

	for(uint32 i = 0; i < iLength1; ++i)
		if( _str1[i] != _str2[i] )
			return false;

	return true;
}

/**
 * \fn bool operator!= (const StringRef & _str1, const String & _str2);
 * \param _str1 first string to compare
 * \param _str2 second string to compare
 * \return true if _str1 and _str2 are not equals
 */
bool operator!= (const StringRef & _str1, const String & _str2)
{
	return !( _str1 == _str2 );
}

/**
 * \fn bool operator== (const String & _str1, const StringRef & _str2);
 * \param _str1 first string to compare
 * \param _str2 second string to compare
 * \return true if _str1 and _str2 are equals
 */
bool operator== (const String & _str1, const StringRef & _str2)
{
	return ( _str2 == _str1 );
}

/**
 * \fn bool operator!= (const String & _str1, const StringRef & _str2);
 * \param _str1 first string to compare
 * \param _str2 second string to compare
 * \return true if _str1 and _str2 are not equals
 */
bool operator!= (const String & _str1, const StringRef & _str2)
{
	return !( _str2 == _str1 );
}

/**
 * \fn bool operator== (const StringRef & _str1, const char * _str2);
 * \param _str1 first string to compare
 * \param _str2 second string to compare
 * \return true if _str1 and _str2 are equals
 */
bool operator== (const StringRef & _str1, const char * _str2)
{
	uint32 iLength1 = _str1.GetLength();
	uint32 iLength2 = _str2 ? strlen(_str2) : 0;

	if( iLength1 != iLength2 )
		return false;

	for(uint32 i = 0; i < iLength1; ++i)
		if( _str1[i] != _str2[i] )
			return false;

	return true;
}

/**
 * \fn bool operator!= (const StringRef & _str1, const char * _str2);
 * \param _str1 first string to compare
 * \param _str2 second string to compare
 * \return true if _str1 and _str2 are not equals
 */
bool operator!= (const StringRef & _str1, const char * _str2)
{
	return !( _str1 == _str2 );
}

/**
 * \fn bool operator== (const char * _str1, const StringRef & _str2);
 * \param _str1 first string to compare
 * \param _str2 second string to compare
 * \return true if _str1 and _str2 are equals
 */
bool operator== (const char * _str1, const StringRef & _str2)
{
	return ( _str2 == _str1 );
}

/**
 * \fn bool operator!= (const char * _str1, const StringRef & _str2);
 * \param _str1 first string to compare
 * \param _str2 second string to compare
 * \return true if _str1 and _str2 are not equals
 */
bool operator!= (const char * _str1, const StringRef & _str2)
{
	return !( _str2 == _str1 );
}

